class Date {
  late DateTime _date;

  DateTime get val => _date;
  List<String Function(String, DateTime)> _formatMap = [
    (String str, DateTime date) {
      return str.replaceAll(RegExp(r'yyyy|YYYY'), date.year.toString());
    },
    (String str, DateTime date) {
      return str.replaceAll(
          RegExp(r'yy|YY'), date.year.toString().substring(2));
    },
    (String str, DateTime date) {
      int _month = date.month;
      String m = completion(_month);
      return str.replaceAll(RegExp(r'MM'), m);
    },
    (String str, DateTime date) {
      return str.replaceAll(RegExp(r'M'), date.month.toString());
    },
    (String str, DateTime date) {
      int _day = date.day;
      String d = completion(_day);
      return str.replaceAll(RegExp(r'dd|DD'), d);
    },
    (String str, DateTime date) {
      return str.replaceAll(RegExp(r'd|D'), date.day.toString());
    },
    (String str, DateTime date) {
      int _hour = date.hour;
      String h = completion(_hour);
      return str.replaceAll(RegExp(r'hh|HH'), h);
    },
    (String str, DateTime date) {
      return str.replaceAll(RegExp(r'h|H'), date.hour.toString());
    },
    (String str, DateTime date) {
      int _minute = date.minute;
      String m = completion(_minute);
      return str.replaceAll(RegExp(r'mm'), m);
    },
    (String str, DateTime date) {
      return str.replaceAll(RegExp(r'm'), date.minute.toString());
    },
    (String str, DateTime date) {
      int _second = date.second;
      String s = completion(_second);
      return str.replaceAll(RegExp(r'ss|SS'), s);
    },
    (String str, DateTime date) {
      return str.replaceAll(RegExp(r's|S'), date.second.toString());
    },
  ];

  Date(DateTime date) {
    _date = date;
  }

  Date.now() {
    _date = DateTime.now();
  }

  Date.parse(String formattedString) {
    _date = DateTime.parse(formattedString);
  }

  Date add(Duration duration) {
    return new Date(_date.add(duration));
  }

  Date subtract(Duration duration) {
    return new Date(_date.subtract(duration));
  }

  String format(String format) {
    String dateStr = format;
    _formatMap.forEach((formatter) {
      dateStr = formatter(dateStr, _date);
    });
    return dateStr;
  }

  DateDiff difference(Date date) {
    Duration dur = this._date.difference(date.val);
    return DateDiff(dur);
  }

  static String completion(num number) {
    String n = number.toString();
    if (number < 10) {
      n = '0' + number.toString();
    }
    return n;
  }
}

class DateDiff {
  int _dayUnit = Duration.secondsPerDay;
  int _hourUnit = Duration.secondsPerHour;
  int _minUnit = Duration.secondsPerMinute;
  late Duration _duration;
  late num inSeconds;
  late num inMinutes;
  late num inHours;
  late num inDays;
  num durSeconds = 0;
  num durMinutes = 0;
  num durHours = 0;
  num durDays = 0;

  DateDiff(Duration duration) {
    this._duration = duration;
    init();
    initDur();
  }

  init() {
    inSeconds = _duration.inSeconds;
    inMinutes = inSeconds / _minUnit;
    inHours = inSeconds / _hourUnit;
    inDays = inSeconds / _dayUnit;
  }

  initDur() {
    num _inSeconds = inSeconds.abs();
    if (_inSeconds >= _dayUnit + _hourUnit) {
      durDays = (_inSeconds / _dayUnit).floor();
      num hoursSec = _inSeconds - durDays * _dayUnit;
      durHours = (hoursSec / _hourUnit).floor();
      num minsSec = hoursSec - durHours * _hourUnit;
      durMinutes = (minsSec / _minUnit).floor();
      durSeconds = (minsSec - durMinutes * _minUnit).floor();
    } else if (_inSeconds >= _hourUnit && _inSeconds < _dayUnit + _hourUnit) {
      durHours = (_inSeconds / _hourUnit).floor();
      num minsSec = _inSeconds - durHours * _hourUnit;
      durMinutes = (minsSec / _minUnit).floor();
      durSeconds = (minsSec - durMinutes * _minUnit).floor();
    } else if (_inSeconds >= _minUnit && _inSeconds < _hourUnit) {
      durMinutes = (_inSeconds / _minUnit).floor();
      durSeconds = (_inSeconds - durMinutes * _minUnit).floor();
    } else {
      durSeconds = _inSeconds.floor();
    }
  }
}
